<?php

namespace App\Library;

use App\Exceptions\StandardException;
use GuzzleHttp\Client;
use Illuminate\Support\Arr;
use OSS\OssClient;
use Ramsey\Uuid\Uuid;
use Log;

class OSS
{
    private static $instance = [];

    private $config;
    private $schema;

    /**
     * @param string $type
     * @return static
     * @throws
     */
    public static function instance(string $type = 'file')
    {
        if (!Arr::has(static::$instance, $type)) {
            static::$instance[$type] = new static($type);
        }

        return static::$instance[$type];
    }

    /**
     * Manager constructor.
     * @param string $type
     * @throws StandardException
     * @throws \OSS\Core\OssException
     */
    private function __construct(string $type)
    {
        $this->config = static::getConfig($type);
        $this->schema = $this->config['ssl'] ? 'https' : 'http';
    }

    private function getClient()
    {
        return new OssClient($this->config['access_key_id'], $this->config['access_key_secret'], $this->config['endpoint_internal']);
    }

    /**
     * @param string $path
     * @param bool $internal
     * @param string $style
     * @return string
     * @throws
     */
    public function getUrl(?string $path, bool $internal = false, ?string $style = null)
    {
        if (empty($path)) {
            return null;
        }
        if (!is_null($style)) {
            $styledPath = $path . $this->config['style_separator'] . $style;
        } else {
            $styledPath = $path;
        }

        if ($internal) {
            return $this->getClient()->signUrl($this->config['bucket'], $styledPath, 3600, OssClient::OSS_HTTP_GET);
        } elseif (!is_null($this->config['cdn'])) {
            $host = $this->schema . '://' . $this->config['cdn'];
            $url = $host . '/' . str_replace(array('%2F', '%25'), array('/', '%'), rawurlencode($styledPath));

            if ($this->config['cdn_auth_key']) {
                $uri = parse_url($url, PHP_URL_PATH);
                $timestamp = now()->timestamp + 3600;
                $rand = Uuid::uuid4()->getHex();
                $sign = md5("{$uri}-{$timestamp}-{$rand}-0-{$this->config['cdn_auth_key']}");
                $url = $url . '?auth_key=' . "{$timestamp}-{$rand}-0-{$sign}";
            }

            return $url;
        } else {
            $client = new OssClient($this->config['access_key_id'], $this->config['access_key_secret'], $this->config['endpoint_external']);
            $client->setUseSSL(true);

            $options = [];

            if (!is_null($style)) {
                $options[OssClient::OSS_PROCESS] = 'style/' . $style;
            }
            return "https://".$this->config['bucket'].".".$this->config['endpoint_external']."/".$path;
//            return $client->signUrl($this->config['bucket'], $path, 3600, OssClient::OSS_HTTP_GET, $options);
        }
    }

    /**e
     * @param string $key
     * @param string $path
     * @throws \OSS\Core\OssException
     */
    public function storeFile(string $key, string $path)
    {
        $this->getClient()->uploadFile($this->config['bucket'], $key, $path);
    }

    /**
     * @param string $key
     * @param string $content
     */
    public function storeBinary(string $key, string $content)
    {
        $this->getClient()->putObject($this->config['bucket'], $key, $content);
    }

    /**
     * @param string $key
     * @param string $mediaId
     */
    public function storeWeChatImage(string $key, string $mediaId)
    {
        $fileContent = app('wechat.official_account')->media->get($mediaId)->getBodyContents();
        $this->storeBinary($key, $fileContent);
    }

    /**
     * @param string $key
     * @return string|null
     * @throws \Exception
     */
    public function copyToWeChat(string $key)
    {
        $fileContent = file_get_contents(internal_file_url($key));

        $client = new Client();
        $accessToken = app('wechat.official_account')->access_token->getToken()['access_token'];
        $filename = Uuid::uuid4()->getHex();
        $response = $client->post('https://api.weixin.qq.com/cgi-bin/media/upload?access_token=' . $accessToken . "&type=image", [
            'multipart' => [
                [
                    'name' => 'media',
                    'filename' => $filename,
                    'contents' => $fileContent,
                ],
            ]
        ])->getBody()->getContents();
        $response = json_decode($response);

        return $response->media_id ?? null;
    }

    /**
     * @param string $prefix
     * @param int $max
     * @return \Illuminate\Support\Collection
     * @throws \OSS\Core\OssException
     */
    public function listFiles(string $prefix = '', $max = 300)
    {
        $options = array(
            'prefix' => $prefix,
            'max-keys' => $max,
            'delimiter' => '/',
        );

        $listObjectInfo = $this->getClient()->listObjects($this->config['bucket'], $options);
        $objectList = $listObjectInfo->getObjectList();

        return collect($objectList)->map->getKey();
    }

    public function copyFile(string $source, string $target)
    {
        $this->getClient()->copyObject($this->config['bucket'], $source, $this->config['bucket'], $target);
    }

    /**
     * @param string $key
     * @return array
     */
    public function getSign(string $key)
    {
        $now = now();
        $config = [
            'expiration' => $now->format('Y-m-d\TH:i:s\Z'),
            'conditions' => [
                [
                    'content-length-range',
                    0,
                    10485760000,
                ], [
                    'eq',
                    '$key',
                    $key,
                ]
            ],
        ];

        $end = $now->addMinutes(5)->format('Y-m-d\TH:i:s\Z');
        $policy = base64_encode(json_encode($config));
        $signature = base64_encode(hash_hmac('sha1', $policy, $this->config['access_key_secret'], true));

        return [
            'accessKeyId' => $this->config['access_key_id'],
            'host' => $this->schema."://{$this->config['bucket']}."."{$this->config['endpoint_external']}/",
            'policy' => $policy,
            'signature' => $signature,
            'expire' => $end,
            'key' => $key,
            'url' => $this->getUrl($key),
        ];
    }

    /**
     * @param string $store
     * @return array
     * @throws StandardException
     */
    private static function getConfig(string $store)
    {
        switch ($store) {
            case 'file':
                $config = config('aliyun.oss.default');
                break;
            case "video":
                $config = config('aliyun.oss.default');
                break;
            default:
                throw new StandardException('file store not fount');
        }

        $config['access_key_id'] = config('aliyun.access_key_id');
        $config['access_key_secret'] = config('aliyun.access_key_secret');
        return $config;
    }
}
